using System.Diagnostics.CodeAnalysis;

namespace Viyi.Util;

public static class ViyiToolkit {
    public static T SuppressException<T>(this Func<T> fn, Func<T> alternate) {
        try { return fn(); }
        catch { return alternate(); }
    }

    public static T SuppressException<T, E>(this Func<T> fn, Func<T> alternate) where E : Exception {
        try { return fn(); }
        catch (E) { return alternate(); }
    }

    public static T SuppressException<T>(this Func<T> fn, Func<Exception, bool> predicate, Func<T> alternate) {
        try { return fn(); }
        catch (Exception e) when (predicate(e)) {
            return alternate();
        }
    }

    public static T SuppressException<T>(this Func<T> fn, Type exceptionType, Func<T> alternate) =>
        SuppressException(fn, e => exceptionType.IsAssignableFrom(e.GetType()), alternate);

    [return: NotNullIfNotNull(nameof(alternate))]
    public static T? SuppressException<T, E>(this Func<T> fn, T? alternate = default) where E : Exception {
        try { return fn(); }
        catch (E) { return alternate; }
    }

    [return: NotNullIfNotNull(nameof(alternate))]
    public static T? SuppressException<T>(this Func<T> fn, T? alternate = default) {
        try { return fn(); }
        catch { return alternate; }
    }

    [return: NotNullIfNotNull(nameof(alternate))]
    public static T? SuppressException<T>(this Func<T> fn, Func<Exception, bool> predicate, T? alternate = default) {
        try { return fn(); }
        catch (Exception e) when (predicate(e)) {
            return alternate;
        }
    }

    [return: NotNullIfNotNull(nameof(alternate))]
    public static T? SuppressException<T>(this Func<T> fn, Type exceptionType, T? alternate = default) =>
        SuppressException(fn, e => exceptionType.IsAssignableFrom(e.GetType()), alternate);
}
